#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "../log/log.h"
#include "../global.h"
#include "../config.h"
#include "../ncis/ncis.h"
#include "../ftal/ftal.h"
#include "sharedmemory.h"


SM_zone_s SM_zones[SM_NB_ZONES] = {
  {  1, 1, NULL }, // Listening child
  { CHILD_NCIS_MAX, sizeof(ncis_standard_s), NULL }, // NCIS -> father
  { CHILD_NCIS_MAX, sizeof(ncis_standard_s), NULL }, // father -> NCIS
  { CHILD_FTAL_RX_MAX, sizeof(ftal_tx_report), NULL }, // FTAL rx
  { CHILD_FTAL_TX_MAX, sizeof(ftal_rx_report), NULL }  // FTAL tx
};


int SM_init(void)
{
  int i, j;
  int fd;
  unsigned int size;
  void* ptr;
  
  if( (fd = shm_open(SM_FILENAME, O_CREAT|O_RDWR, 0600)) < 0 )
  {
    LOG_add(LOG_FATAL, "Cannot create shared memory: %s", strerror(errno) );
    return -1;
  }

  // Calculate size of needed memory
  size = 0;
  for( i=0; i<SM_NB_ZONES; i++ )
    size += SM_zones[i].size *
      (ALIGN(sizeof(SM_elem_s)) + ALIGN(SM_zones[i].data_size));

  if( ftruncate(fd, size) < 0 )
  {
    LOG_add(LOG_FATAL, "Cannot size shared memory: %s", strerror(errno) );
    return -1;
  }

  ptr = mmap( NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
  if( ptr == MAP_FAILED )
  {
    LOG_add(LOG_FATAL, "Cannot map shared memory: %s", strerror(errno) );
    return -1;
  }

  close(fd);
  

  // Initialize zones and elements
  for( i=0; i<SM_NB_ZONES; i++ )
  {
    SM_zones[i].elems = ptr;
    ptr += SM_zones[i].size * ALIGN(sizeof(SM_elem_s));

    for( j=0; j<SM_zones[i].size; j++ )
    {
      SM_zones[i].elems[j].state = SM_FREE;
      SM_zones[i].elems[j].data  = ptr;

      ptr += ALIGN(SM_zones[i].data_size);
    }
  }

  return 0;
}


int SM_close(void)
{
  int i;
  unsigned int size = 0;

  // Calculate size of needed memory
  for( i=0; i<SM_NB_ZONES; i++ )
    size += SM_zones[i].size *
    (ALIGN(sizeof(SM_elem_s)) + ALIGN(SM_zones[i].data_size));

  return munmap(SM_zones[0].elems, size);
}

int SM_destroy(void)
{
  return shm_unlink(SM_FILENAME);
}


SM_elem_s* SM_alloc(SM_zone_s* zone)
{
  int i;

  for( i=0; i<zone->size; i++ )
    if( zone->elems[i].state == SM_FREE )
    {
      SM_set_empty(&zone->elems[i]);
      return &zone->elems[i];
    }

  return NULL;
}

inline void SM_free(SM_elem_s* elem)  { elem->state = SM_FREE; elem->pid = 0; }

inline void SM_set_full(SM_elem_s* elem)  { elem->state = SM_FULL; }
inline void SM_set_empty(SM_elem_s* elem) { elem->state = SM_EMPTY; }

